﻿using System.Collections.Specialized;
using System.IdentityModel.Tokens;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.Web;
using Microsoft.IdentityModel.Claims;
using Microsoft.IdentityModel.SecurityTokenService;
using System;
using System.Reflection;

namespace Microsoft.IdentityModel.OAuth
{
    [ServiceContract]
    internal interface IOAuthIssuerContract
    {
        [WebInvoke]
        Stream Issue(Stream request);
        [WebGet(UriTemplate = "/clientaccesspolicy.xml")]
        Stream SilverlightCrossDomainPolicy();
    }

    /// <summary>
    /// This class provides OAuth protocol head for the WIF token issuance framework. 
    /// </summary>
    internal class OAuthIssuerContract : IOAuthIssuerContract
    {
        public Stream Issue(Stream request)
        {
            var formPOST = new StreamReader(request).ReadToEnd();
            NameValueCollection nvc = HttpUtility.ParseQueryString(formPOST);

            var wifConfig = ((OAuthServiceHost)OperationContext.Current.Host).Configuration;

            var inputToken = CreateInputAuthenticationToken(nvc);
            var inputClaimsIdentities = wifConfig.SecurityTokenHandlers.ValidateToken(inputToken);

            var rst = OAuthSerializer.CreateRequest(nvc);
            rst.RequestType = RequestTypes.Issue;
            rst.TokenType = SWTSecurityTokenHandler.TokenTypeIdentifier;

            // Start WIF token issuance pipeline...
            var rstr = wifConfig.CreateSecurityTokenService().Issue(new ClaimsPrincipal(inputClaimsIdentities), rst);

            var swt = rstr.RequestedSecurityToken.SecurityToken as SimpleWebToken;

            if (swt == null) //TODO:Fix exception
                throw new WebFaultException(System.Net.HttpStatusCode.BadRequest);

            var response = "wrap_access_token=" + swt.RawToken.UrlEncode();

            return new MemoryStream(Encoding.ASCII.GetBytes(response));
        }

        private SecurityToken CreateInputAuthenticationToken(NameValueCollection nvc)
        {
            if (nvc.Keys.Contains("wrap_name") && nvc.Keys.Contains("wrap_password"))
                return new UserNameSecurityToken(nvc["wrap_name"], nvc["wrap_password"]);

            // TODO: add support for input SWTs & Saml
            throw new NotSupportedException("Authentication tokens other than UserName/Password are currently not supported.");
        }

        public Stream SilverlightCrossDomainPolicy()
        {
            return Assembly.GetExecutingAssembly().GetManifestResourceStream("Microsoft.IdentityModel.OAuth.clientaccesspolicy.xml");
        }
    }
}
